package ws

import (
	"github.com/gorilla/websocket"
	errs2 "github.com/zeebo/errs"
	"go.uber.org/zap"
	"sync"
)

// 所属分组按位运算
type Group uint64

const (
	WsGroupAlarmPopup Group = 1
	WsGroupAlarmOther Group = 1 << 2
)

type Manage struct {
	gMux    sync.Mutex
	log     *zap.Logger
	clients sync.Map
	routes  sync.Map
}

func NewManage(log *zap.Logger) *Manage {
	h := &Manage{
		log: log,
	}
	//go func() {
	//	timer.RunTickerAfter(context.Background(), time.Second*2, func() error {
	//		len := 0
	//		cc := make([]string, 0)
	//		m.clients.Range(func(key, value any) bool {
	//			len++
	//			cc = append(cc, value.(*Client).ID())
	//			return true
	//		})
	//		fmt.Println("ws length: ", len)
	//		fmt.Println("ws clients: ", strings.Join(cc, ";"))
	//		return nil
	//	})
	//}()
	return h
}

// Broadcast 广播发送ws消息
func (m *Manage) Broadcast(msg *Msg) error {
	errs := errs2.Group{}
	m.clients.Range(func(key, value any) bool {
		errs.Add(value.(*client).send(msg))
		return true
	})
	return errs.Err()
}

func (m *Manage) Groupcast(group Group, msg *Msg) error {
	var g []*client
	m.gMux.Lock()
	m.clients.Range(func(key, value any) bool {
		if value.(*client).group&group == group {
			g = append(g, value.(*client))
		}
		return true
	})
	m.gMux.Unlock()
	errs := errs2.Group{}
	for i := range g {
		errs.Add(g[i].send(msg))
	}
	return errs.Err()
}

func (m *Manage) AddGroup(id string, group Group) {
	if c, ok := m.clients.Load(id); ok {
		m.gMux.Lock()
		c.(*client).group |= group
		m.gMux.Unlock()
	}
}

func (m *Manage) RemoveGroup(id string, group Group) {
	if c, ok := m.clients.Load(id); ok {
		m.gMux.Lock()
		c.(*client).group &= ^group
		m.gMux.Unlock()
	}
}

// Send 单独发送一个消息
func (m *Manage) Send(id string, msg *Msg) error {
	if c, ok := m.clients.Load(id); ok && c.(*client) != nil {
		return c.(*client).send(msg)
	}
	return ErrClosed
}

// Register 添加新的ws
func (m *Manage) Register(conn *websocket.Conn) {
	c := client{
		manage:    m,
		log:       m.log,
		conn:      conn,
		msgChan:   make(chan *Msg, 10),
		closeChan: make(chan byte),
	}
	c.procLoop() //启动监听
	m.clients.Store(c.ID(), &c)
	return
}

// Unregister 移除ws
func (m *Manage) Unregister(id string) {
	m.clients.Delete(id)
	return
}

func (m *Manage) msgHandle(cid string, msg *Msg) {
	if fn, ok := m.routes.Load(msg.Action); ok {
		if fn.(MsgHandle) != nil {
			fn.(MsgHandle)(cid, msg)
		}
	}
}

func (m *Manage) Route(action Action, handle MsgHandle) {
	m.routes.Store(action, handle)
}
